﻿using System;
using System.Drawing;
using System.IO;

namespace OpenShape
{
    class Shapefile
    {
        public Shapefile()  // 생성자
        {
        }

        public string m_strSHPPath;
        public string m_strSHXPath;


        public bool LoadData(string pShp, Layer ly)
        {
            
            FileStream st_SHP;
            FileStream st_SHX;
            string pShx;

            pShx = pShp.Substring(0, pShp.IndexOf(".")) + ".shx";
            m_strSHPPath = pShp ;
            m_strSHXPath = pShx ;
            
            st_SHP = new FileStream(pShp, FileMode.Open, FileAccess.Read, FileShare.Read);
            st_SHX = new FileStream(pShx, FileMode.Open, FileAccess.Read, FileShare.Read);


            // 파일 사이즈 및 레코드 개수 
            byte[] temp_size = fRead(st_SHX, 24, 4);

            SwapArray(temp_size); //(Big -> Little order로 변환)
            int temp;
            temp = BitConverter.ToInt32(temp_size, 0);

            int m_nFileSize = temp * 2;  // 파일 사이즈
            ly.m_nRecords = (m_nFileSize - 100) / 8; // 레코드의 개수를 구함

            // Shp의 도형 타입 (1 = point, 3 = polyline, 5 = polygon )
            byte[] temp_type = fRead(st_SHP, 32, 4);
            ly.m_nShapeType = BitConverter.ToInt32(temp_type, 0);

            // MBR구하기
            byte[] temp_Xmin = fRead(st_SHP, 36, 8);
            ly.m_MBR.xmin = BitConverter.ToDouble(temp_Xmin, 0);
            byte[] temp_Ymin = fRead(st_SHP, 44, 8);
            ly.m_MBR.ymin = BitConverter.ToDouble(temp_Ymin, 0);
            byte[] temp_Xmax = fRead(st_SHP, 52, 8);
            ly.m_MBR.xmax = BitConverter.ToDouble(temp_Xmax, 0);
            byte[] temp_Ymax = fRead(st_SHP, 60, 8);
            ly.m_MBR.ymax = BitConverter.ToDouble(temp_Ymax, 0);


            // 객체읽기 (포인트, 폴리라인, 폴리곤)

            switch (ly.m_nShapeType)
            {
                case 1: //Point
                    GeoPoint[] SHPPoints = new GeoPoint[ly.m_nRecords];

                    for (int i = 0; i < ly.m_nRecords; i++)
                    {
                        SHPPoints[i] = new GeoPoint();
                        byte[] tempN_byte = fRead(st_SHX, 100 + i * 8, 4);
                        SwapArray(tempN_byte);
                        int tempN = BitConverter.ToInt32(tempN_byte, 0);
                        tempN = tempN * 2;

                        st_SHP.Seek(tempN + 12, SeekOrigin.Begin);

                        byte[] temp_PointsX = new byte[8];
                        byte[] temp_PointsY = new byte[8];
                        st_SHP.Read(temp_PointsX, 0, 8);
                        st_SHP.Read(temp_PointsY, 0, 8);

                        SHPPoints[i].Points.Add(new PointF((float)BitConverter.ToDouble(temp_PointsX, 0), (float)BitConverter.ToDouble(temp_PointsY, 0)));
                    }
                    ly.m_SpatialObject.AddRange(SHPPoints);
                    break;

                case 3:
                    
                    GeoPolyline[] SHPPolyline = new GeoPolyline[ly.m_nRecords];

                    for (int i = 0; i < ly.m_nRecords; i++)
                    {
                        SHPPolyline[i] = new GeoPolyline();
                        byte[] tempN_byte = fRead(st_SHX, 100 + i * 8, 4);
                        SwapArray(tempN_byte);
                        int tempN = BitConverter.ToInt32(tempN_byte, 0);
                        tempN = tempN * 2;

                        st_SHP.Seek(tempN + 12, SeekOrigin.Begin);
                        
                        byte[] temp_Rect = new byte[32];  // get MBR
                        st_SHP.Read(temp_Rect, 0, 32);
                        SHPPolyline[i].SetRect(temp_Rect);

                        byte[] temp_NPart = new byte[4];
                        st_SHP.Read(temp_NPart, 0, 4);
                        SHPPolyline[i].NumParts = BitConverter.ToInt32(temp_NPart,0);

                        byte[] temp_NPoints = new byte[4];
                        st_SHP.Read(temp_NPoints, 0, 4);
                        SHPPolyline[i].NumPoints = BitConverter.ToInt32(temp_NPoints,0);

                        for (int j = 0; j < SHPPolyline[i].NumParts; j++)
                        {
                            byte[] temp_Parts = new byte[4];
                            st_SHP.Read(temp_Parts, 0, 4);
                            SHPPolyline[i].Parts.Add(BitConverter.ToInt32(temp_Parts,0));
                        }

                        for (int j = 0; j<SHPPolyline[i].NumPoints; j++)
                        {
                            byte[] temp_PointsX = new byte[8];
                            byte[] temp_PointsY = new byte[8];
                            st_SHP.Read(temp_PointsX, 0, 8);
                            st_SHP.Read(temp_PointsY, 0, 8);

                            SHPPolyline[i].Points.Add(new PointF((float)BitConverter.ToDouble(temp_PointsX, 0), (float)BitConverter.ToDouble(temp_PointsY, 0)));
                        }
                    }
                    ly.m_SpatialObject.AddRange(SHPPolyline);
                    break;

                case 5:
                    GeoPolygon[] SHPPolygon = new GeoPolygon[ly.m_nRecords];

                    for (int i = 0; i < ly.m_nRecords; i++)
                    {
                        SHPPolygon[i] = new GeoPolygon();
                        byte[] tempN_byte = fRead(st_SHX, 100 + i * 8, 4);
                        SwapArray(tempN_byte);
                        int tempN = BitConverter.ToInt32(tempN_byte, 0);
                        tempN = tempN * 2;

                        st_SHP.Seek(tempN + 12, SeekOrigin.Begin);

                        byte[] temp_Rect = new byte[32];  // get MBR
                        st_SHP.Read(temp_Rect, 0, 32);
                        SHPPolygon[i].SetRect(temp_Rect);

                        byte[] temp_NPart = new byte[4];
                        st_SHP.Read(temp_NPart, 0, 4);
                        SHPPolygon[i].NumParts = BitConverter.ToInt32(temp_NPart, 0);

                        byte[] temp_NPoints = new byte[4];
                        st_SHP.Read(temp_NPoints, 0, 4);
                        SHPPolygon[i].NumPoints = BitConverter.ToInt32(temp_NPoints, 0);

                        for (int j = 0; j < SHPPolygon[i].NumParts; j++)
                        {
                            byte[] temp_Parts = new byte[4];
                            st_SHP.Read(temp_Parts, 0, 4);
                            SHPPolygon[i].Parts.Add(BitConverter.ToInt32(temp_Parts, 0));
                        }

                        for (int j = 0; j < SHPPolygon[i].NumPoints; j++)
                        {
                            byte[] temp_PointsX = new byte[8];
                            byte[] temp_PointsY = new byte[8];
                            st_SHP.Read(temp_PointsX, 0, 8);
                            st_SHP.Read(temp_PointsY, 0, 8);

                            SHPPolygon[i].Points.Add(new PointF((float)BitConverter.ToDouble(temp_PointsX, 0), (float)BitConverter.ToDouble(temp_PointsY, 0)));
                        }
                    }
                    ly.m_SpatialObject.AddRange(SHPPolygon);
                    break;
                default:
                    break;
            }
                

            st_SHP.Dispose();
            st_SHX.Dispose();

            return true;

        }

        public byte[] SwapArray(byte[] pByte)  //(Big -> Little order로 변환)
        {
            byte temp_n;
            for (int i = 0; i < pByte.Length / 2; i++)
            {
                temp_n = pByte[pByte.Length - i - 1];
                pByte[pByte.Length - i - 1] = pByte[i];
                pByte[i] = temp_n;
            }
            return pByte;
        }

        public byte[] fRead(FileStream fs, int s_n, int c)
        {
            byte[] ByteArray = new byte[c];
            fs.Seek(s_n, SeekOrigin.Begin);
            fs.Read(ByteArray, 0, c);
            return ByteArray;
        }
    }
}
